package com.gosuncn.config.pdf;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public abstract class AbstractHtmlToPdf {

    private static Logger logger = LoggerFactory.getLogger(AbstractHtmlToPdf.class);

    protected String toolPath;

    // 初始化块
    {
        toolPath = init();
        logger.info("初始化工具wkhtmltopdf工具完成！路径：{}", toolPath);
        // 执行初始化命令
        initCommand();
    }

    /**
     * 获取不同系统的工具
     * @return
     */
    public abstract SystemTool getSystemTool();

    /**
     * 不同的系统自己执行需要初始化的命令，
     * 比如Linux系统需要为工具授予执行权限
     */
    public abstract void initCommand();

    /**
     * 批量生成PDF文件
     * @param urls
     * @return
     */
    public List<File> batchGeneratePdf(List<String> urls) {
        List<File> files = new ArrayList<>();
        if (urls != null && !urls.isEmpty()) {
            int threadPoolSize = 0;
            int listSize = urls.size();
            int availableSize = Runtime.getRuntime().availableProcessors();
            if (listSize > availableSize / 2) {
                threadPoolSize = availableSize / 2;
            } else {
                threadPoolSize = listSize;
            }
            ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
            ExecutorCompletionService<File> completion = new ExecutorCompletionService<>(executor);
            for (final String url : urls) {
                completion.submit(new Callable<File>() {
                    @Override
                    public File call() throws Exception {
                        File pdf = generatePdf(url, 0);
                        return pdf;
                    }
                });
            }
            try {
                for (int i = 0; i < urls.size(); i++) {
                    files.add(completion.take().get());
                }
            } catch (InterruptedException | ExecutionException e) {
                logger.error("批量生成PDF文件出现异常", e);
            }
            executor.shutdown();
        }
        return files;
    }

    /**
     * 生成单个PDF文件
     * @param url
     * @param delay  javascript延迟（单位毫秒）
     * @return
     */
    public File generatePdf(String url, Integer delay) {
        String folder = System.getProperty("java.io.tmpdir");
        File pdf = new File(folder, java.util.UUID.randomUUID() + ".pdf");
        StringBuilder sb = new StringBuilder(toolPath);
        sb.append(" ").append(url);
        if (delay != null && delay > 0) {
            sb.append(" ").append("--javascript-delay ").append(delay);
        }
        String command = sb.append(" ").append(pdf.getAbsolutePath()).toString();
        System.out.println(command);
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(command);
            ProcessStreamHandler.handle(process);
            process.waitFor();
            if (!pdf.exists()) {
                pdf = null;
            }
        } catch (IOException | InterruptedException e) {
            logger.error("生成PDF文件出现异常", e);
            if (pdf.exists()) {
                pdf.delete();
                pdf = null;
            }
            if (process != null) {
                process.destroy();
            }
        }
        return pdf;
    }

    /**
     * 生成单个PDF文件
     * @param url
     * @param delay  javascript延迟（单位毫秒）
     * @return
     */
    public byte[] generatePdfToByteArray(String url, Integer delay) {
        File pdf = this.generatePdf(url, delay);
        byte[] result = null;
        if (pdf != null) {
            try (InputStream in = new FileInputStream(pdf); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                byte[] bytes = new byte[1024];
                int length = 0;
                while ((length = in.read(bytes)) != -1) {
                    baos.write(bytes, 0, length);
                }
                result = baos.toByteArray();
            } catch (IOException e) {
                logger.error("生成单个PDF文件出现异常", e);
            }
            pdf.delete();
        }
        return result;
    }

    /**
     * 初始化将jar包中的工具解压出来
     * @return
     */
    private String init() {
        String projectPath = ClassUtils.getDefaultClassLoader().getResource("wkhtmltopdf").getPath();
        String toolName = this.getSystemTool().getToolName();
        if (toolName == null) {
            return "wkhtmltopdf";
        }
        File tool = null;
        // 如果是打包环境下，自动将工具解压到与jar包同一个目录
        if (projectPath.contains(".jar!")) {
            File folder = new File("./", "wkhtmltopdf");
            if (!folder.exists()) {
                folder.mkdirs();
            }
            tool = new File(folder, toolName);
            try (InputStream is = ResourceUtils.getURL("classpath:wkhtmltopdf/" + toolName).openStream();
                    FileOutputStream fos = new FileOutputStream(tool)) {
                byte[] buffer = new byte[1024];
                int lenght = 0;
                while ((lenght = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, lenght);
                }
            } catch (IOException e) {
                logger.error("wkhtmltopdf工具初始化出现异常", e);
            }
            return tool.getAbsolutePath();
        }
        tool = new File(projectPath.substring(1), toolName);
        return tool.getAbsolutePath();
    }

    public String getToolPath() {
        return toolPath;
    }

}
